'''
Created on May 1, 2011

@author: james
'''

from datetime import date

class MonthGenerator():
    
    '''
    Overnight call = 0
    NICU = 1
    Step down = 2
    Clinic = 3
    '''
    
    WEEKDAY_CALL = 0
    FRIDAY_CALL = 1
    WEEKEND_CALL = 2
    
    '''
    __holidayCalendar = list of days in month which are holidays (only shift is call)
    ''' 
    __holidayCalendar = [[1], [], [], [], [30], [], [4], [], [5], [], [24], []];
    __callTypeCycle = [WEEKEND_CALL, WEEKDAY_CALL, WEEKDAY_CALL, WEEKDAY_CALL, WEEKDAY_CALL, FRIDAY_CALL, WEEKEND_CALL]
    __weekCallCycle = [1, 4, 4, 3, 3, 3, 1]
        
    @staticmethod
    def generate(monthNumber, year, debug = False):
        
        '''
        numberDays = number of days in month
        firstDayOfMonth = day of week of month start, 1 = Sunday
        '''
        numberDays = (date(year, monthNumber + 1, 1) - date(year, monthNumber, 1)).days
        firstDayOfMonth = (date(year, monthNumber, 1).weekday() + 1) % 7 + 1
        
        # list is zero based
        monthNumber -= 1
        firstDayOfMonth -= 1 
        weekLength = 7
        
        # we already know about the first week, complete or not
        numberCenterWeeks = ((numberDays - weekLength + firstDayOfMonth) // weekLength)
        remainder = numberDays + firstDayOfMonth - (numberCenterWeeks + 1) * weekLength
        
        if debug: print "firstDayOfMonth %s" % firstDayOfMonth
        if debug: print "numberCenterWeeks %s" % numberCenterWeeks
        if debug: print "remainder %s" % remainder
        
        ''' initialise these with values that sync with first day of month '''
        callTypes = MonthGenerator.__callTypeCycle[firstDayOfMonth:]
        callSchedule = MonthGenerator.__weekCallCycle[firstDayOfMonth:]

        if debug: print "beginning callTypes %s" % callTypes
        if debug: print "beginning callSchedule %s" % callSchedule
        if debug: print "Calls per week %s" % sum(MonthGenerator.__weekCallCycle)
        
        firstSundayOfWeek = [weekLength - firstDayOfMonth]
        if debug: print "firstSundayOfWeek %s" % firstSundayOfWeek
        
        for weekNumber in range(numberCenterWeeks):
            [callSchedule.append(x) for x in MonthGenerator.__weekCallCycle]
            [callTypes.append(x) for x in MonthGenerator.__callTypeCycle]
            firstSundayOfWeek.append((weekLength - firstDayOfMonth) + weekLength * (weekNumber + 1))
        
        callSchedule.extend(MonthGenerator.__weekCallCycle[:remainder])
        callTypes.extend(MonthGenerator.__callTypeCycle[:remainder])

        if debug: print "complete callTypes %s" % callTypes
        if debug: print "complete callSchedule %s" % callSchedule
        if debug: print "complete firstSundayOfWeek %s" % firstSundayOfWeek
        
        holidayCallDays = []
        
        for holiday in MonthGenerator.__holidayCalendar[monthNumber]:
            # list is zero based
            # days which are holidays will only have one call
            callSchedule[holiday - 1] = 1
            holidayCallDays.append(holiday - 1)

        # note that weekday calls includes any holiday calls
        weekdayCallDays = []
        fridayCallDays = []
        weekendCallDays = []
        
        for i in range(len(callTypes)):
            type = callTypes[i]
            
            if type == MonthGenerator.WEEKDAY_CALL:
                weekdayCallDays.append(i)
            elif type == MonthGenerator.FRIDAY_CALL:
                fridayCallDays.append(i)
            elif type == MonthGenerator.WEEKEND_CALL:
                weekendCallDays.append(i)
        
        if debug: print "weekdayCallDays %s" % weekdayCallDays
        if debug: print "fridayCallDays %s" % fridayCallDays
        if debug: print "weekendCallDays %s" % weekendCallDays
        if debug: print "holidayCallDays %s" % holidayCallDays
        
        weekBoundary = []
        last = 0
        for i in firstSundayOfWeek:
            weekBoundary.append ( sum(callSchedule[last:i]) )
            
        if debug: print "weekBoundary %s" % weekBoundary
            
        if debug:
            print callSchedule
            print callTypes
            print len(callSchedule)
            print sum(callSchedule)
            print firstSundayOfWeek
            print weekBoundary
            
        call = []
        nicu = []
        stepDown = []
        clinic = []
        weekendCall = []
        
        lastCallIndex = -1
        lastShiftCount = -1
        totalShiftCount = 0
        
        for i in range(len(callSchedule)):
            shiftCount = callSchedule[i]
            totalShiftCount += shiftCount
            call.append( totalShiftCount - shiftCount + 0)
            
            # find friday calls
            if shiftCount == 1:
                if lastShiftCount != 1 and i + 1 not in holidayCallDays and lastCallIndex != -1:
                    weekendCall.append(lastCallIndex)
                
                weekendCall.append(totalShiftCount - shiftCount)
                
            lastShiftCount = shiftCount
            lastCallIndex = totalShiftCount - shiftCount
            
            if shiftCount > 1:
                nicu.append( totalShiftCount - shiftCount + 1 )
                
            if shiftCount > 2:
                stepDown.append( totalShiftCount - shiftCount + 2 )
            
            if shiftCount > 3:
                clinic.append( totalShiftCount - shiftCount + 3 )
                
        if debug: 
            print "call: %s" % call
            print "weekend call: %s" % weekendCall
            print "nicu: %s" % nicu
            print "step down: %s" % stepDown
            print "clinic: %s" % clinic
            
        return [callSchedule, weekBoundary, 
                call, nicu, stepDown, clinic, weekendCall, 
                weekdayCallDays, fridayCallDays, weekendCallDays, holidayCallDays]


def main():
    g = MonthGenerator()

    #print g.generate(31, 1, [1, 4, 4, 3, 3, 3, 1], [], True)
    print g.generate(31, 1, [1, 4, 4, 3, 3, 3, 1], [1, 0, 0, 0, 0, 2, 1], [30], True)
    #print g.generate(31, 1, [1, 4, 4, 3, 3, 3, 1], [30], True)
    #print g.generate(31, 3, [1, 4, 4, 3, 3, 3, 1], [30], True)
        
       
if __name__ == "__main__":
    main()        
